package aishua.com.mytestapplist.utils;

import android.util.Xml;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;

import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

import aishua.com.mytestapplist.annotation.OtherXmlInject;
import aishua.com.mytestapplist.annotation.OtherXmlRootTag;


public class Converter {
	/**
	 * java bean convert to String
	 * @param java bean include list attribute
	 * @return
	 * @throws Exception
	 */
	public static <T> String convertBean2Str(T t) throws Exception {
		if(null==t){
			return null;
		}
		XmlSerializer ser = Xml.newSerializer();
		StringWriter writer = new StringWriter();
		ser.setOutput(writer);
		ser.startDocument("utf-8", true);
		Method[] methods = t.getClass().getMethods();
		String startTag = null;
		if(t.getClass().isAnnotationPresent(OtherXmlRootTag.class)){
			OtherXmlRootTag otherTag = t.getClass().getAnnotation(OtherXmlRootTag.class);
			startTag = otherTag.tag();
			ser.startTag("", startTag);
			String[] attrs = otherTag.attrs();
			if(attrs.length!=0){
				for(int i = 0;i<attrs.length-1;i+=2){
					ser.attribute("", attrs[i], attrs[i+1]);
				}
			}
		}
		AccessibleObject.setAccessible(methods, true);
		for(Method method:methods){
//			String methodName = method.getName().trim();
			if(method.isAnnotationPresent(OtherXmlInject.class)){//gener xml
//				OtherXmlInject mthod_tag = method.getAnnotation(OtherXmlInject.class);
				Object obj = method.invoke(t, new Object[]{});
				insertTag(t, ser, method,obj);
			}
			
//			if(methodName.startsWith("get")&&!methodName.equals("getClass")){
//				Object obj = method.invoke(t, new Object[]{});
//				insertTag(t, ser, method,obj);
//			}
		} 
		ser.endTag("", startTag);
		ser.endDocument();
		
		return writer.toString();
	}

	private static <T> void insertTag(T t, XmlSerializer ser, Method method, Object value)
			throws IOException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		OtherXmlInject tagInject = null;
		if(method.isAnnotationPresent(OtherXmlInject.class)){
			tagInject = method.getAnnotation(OtherXmlInject.class);
		}else{
			//dote have pass
			return;
//			throw new RuntimeException("");
		}
		if(tagInject!=null){
			String tag = tagInject.tag();
			ser.startTag("", tag);
			String[] attrs = tagInject.attrs();
			if(attrs.length!=0){
				for(int i=0;i<attrs.length-1;i+=2){
					ser.attribute("", attrs[i], attrs[i+1]);
				}
			}
		
		if(value instanceof String){
			insertSingleTag(ser,(String)value);
		}else if(value instanceof List){
				
				List<?> values = (List<?>)value;
				for(Object obj:values){
					boolean isHasRoot = false;
					String startTag = null;
					if(obj.getClass().isAnnotationPresent(OtherXmlRootTag.class)){
						OtherXmlRootTag otherTag = obj.getClass().getAnnotation(OtherXmlRootTag.class);
						startTag = otherTag.tag();
						ser.startTag("", startTag);
						String[] innerAttrs = otherTag.attrs();
						if(innerAttrs.length!=0){
							for(int i = 0;i<attrs.length-1;i+=2){
								ser.attribute("", attrs[i], attrs[i+1]);
							}
						}
						isHasRoot = true;
					}
					Method[] secend_methods = obj.getClass().getMethods();
					AccessibleObject.setAccessible(secend_methods, true);
					for(Method sm:secend_methods){
						if(sm.isAnnotationPresent(OtherXmlInject.class)){
							Object sec_obj = sm.invoke(obj, new Object[]{});
							if(null==sec_obj){
								continue;
							}
							insertTag(obj, ser, sm,sec_obj);
						}
//						String methodName = sm.getName().trim();
//						if(methodName.startsWith("get")&&!methodName.equals("getClass")){
//							Object sec_obj = sm.invoke(obj, new Object[]{});
//							if(null==sec_obj){
//								continue;
//							}
//							insertTag(obj, ser, sm,sec_obj);
//						}
					}
					if(isHasRoot){
						ser.endTag("", startTag);
					}
				}
			}
		ser.endTag("", tag);
		}
	}
	
	
	private static void insertSingleTag(XmlSerializer ser, String value) throws IOException {
			ser.text(value);
	}
	
	/**
	 * add filed has more list<?> parse
	 * @param clazz
	 * @param cls
	 * @param inStream
	 * @return
	 * @throws Exception
	 */
	public static <Outer> Outer getBean(Class<Outer> clazz, List<Class<?>> cls, InputStream inStream)throws Exception {
		
		Outer t = clazz.newInstance();
		Method[] methods = clazz.getMethods();
		AccessibleObject.setAccessible(methods, true);
		XmlPullParser parser = Xml.newPullParser();
//		parser.setInput(sr);
		parser.setInput(inStream, "UTF-8");
		int type = parser.getEventType();
		while(type!= XmlPullParser.END_DOCUMENT){
			switch(type){
			case XmlPullParser.START_TAG:
				String tag = parser.getName();
				for(Method method:methods){
//					if(method.isAnnotationPresent(OtherXmlInject.class)){
//					if(method.getName().startsWith("set")){
						if(method.isAnnotationPresent(OtherXmlInject.class)){
							OtherXmlInject tagxml = method.getAnnotation(OtherXmlInject.class);
							if(tag.endsWith(tagxml.tag())){
									Class<?>[] cla = method.getParameterTypes();
									if(cla[0]==String.class){
										String[] attrs = tagxml.attrs();
										if(attrs.length!=0){
											if(attrs[0].equals(parser.getAttributeName(0))){
												method.invoke(t, new Object[]{parser.getAttributeValue(0)});
											}
										}else{
											method.invoke(t, new Object[]{parser.nextText()});
										}
									}else if(cla[0]==List.class){//list����
										//���ʵ��
										// �ٴ���� ������ǩ����ӵ�List��
										// ���Կ��ܼ��뵽bean��ȥ
										
										
										Type[] ts = method.getGenericParameterTypes();
										Type[] types = ((ParameterizedType) ts[0])
					                            .getActualTypeArguments();

										for(Class<?> c : cls){
											if(c==types[0]){
												type = insertList2Bean(c, t,
														parser, type, method);
												break;
											}
										}

									}
									break;
							}
						}
//					}
				}
				break;
			case XmlPullParser.END_TAG:
				break;
			}
//			parser.nextTag();
			type = parser.next();
		}
		inStream.close();
		return t;
		
		
//		return getBean(clazz, e_clazz, inStream);
	}
	
	public static <Outer,Inner> Outer getBean(Class<Outer> clazz, Class<Inner> e_clazz, InputStream inStream) throws XmlPullParserException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, IOException, InstantiationException {
		Outer t = clazz.newInstance();
		Method[] methods = clazz.getMethods();
		AccessibleObject.setAccessible(methods, true);
		XmlPullParser parser = Xml.newPullParser();
//		parser.setInput(sr);
		parser.setInput(inStream, "UTF-8");
		int type = parser.getEventType();
		while(type!= XmlPullParser.END_DOCUMENT){
			switch(type){
			case XmlPullParser.START_TAG:
				String tag = parser.getName();
				for(Method method:methods){
//					if(method.getName().startsWith("set")){
						if(method.isAnnotationPresent(OtherXmlInject.class)){
							OtherXmlInject tagxml = method.getAnnotation(OtherXmlInject.class);
							if(tag.endsWith(tagxml.tag())){
									Class<?>[] cla = method.getParameterTypes();
									if(cla[0]==String.class){
										String[] attrs = tagxml.attrs();
										if(attrs.length!=0){
											//ע�������ԣ������Ը��Ƶ�bean��
											if(attrs[0].equals(parser.getAttributeName(0))){
												method.invoke(t, new Object[]{parser.getAttributeValue(0)});
											}
										}else{
											method.invoke(t, new Object[]{parser.nextText()});
										}
									}else if(cla[0]==List.class){//list����
										//���ʵ��
										// �ٴ���� ������ǩ����ӵ�List��
										// ���Կ��ܼ��뵽bean��ȥ
										type = insertList2Bean(e_clazz, t,
												parser, type, method);
									}
									break;
							}
						}
//					}
				}
				break;
			case XmlPullParser.END_TAG:
				break;
			}
//			parser.nextTag();
			type = parser.next();
		}
		inStream.close();
		return t;
	}
	private static <T, E> int insertList2Bean(Class<E> e_clazz, T t,
											  XmlPullParser parser, int type, Method method)
			throws XmlPullParserException, IOException, InstantiationException,
			IllegalAccessException, InvocationTargetException {
		List<E> nodes = new ArrayList<E>();
		if(e_clazz.isAnnotationPresent(OtherXmlRootTag.class)){
			OtherXmlRootTag iInsideTag = e_clazz.getAnnotation(OtherXmlRootTag.class);
			String insideTag = iInsideTag.tag();
			boolean flag = true;
			while(flag){
				type = parser.nextTag();
				if(insideTag.equals(parser.getName())){
					//��ȡ   tagȻ����뼯����
					type = parser.nextTag();
					Method[] child_ms = e_clazz.getMethods();
					AccessibleObject.setAccessible(child_ms, true);
					OtherXmlRootTag tagFalg = e_clazz.getAnnotation(OtherXmlRootTag.class);
					E e = e_clazz.newInstance();
					while(!parser.getName().equals(tagFalg.tag())){
						for(Method m:child_ms){
//							if(m.getName().startsWith("set")){
								if(m.isAnnotationPresent(OtherXmlInject.class)){
									OtherXmlInject mTagAnn = m.getAnnotation(OtherXmlInject.class);
									if(mTagAnn.tag().equals(parser.getName())){
										m.invoke(e, new Object[]{parser.nextText()});
										break;
									}
								}
//							}
						
						}
//						type = parser.next();
						try{
							type = parser.nextTag();
						}catch(Exception tagExep){
							type = parser.nextTag();
						}
					}
					nodes.add(e);
					
				}else{
					flag = false;
				}
			}
		}
		method.invoke(t, new Object[]{nodes});
		return type;
	}
	
	public static <T> List<T> getBeans(Class<T> clazz, InputStream is){
		List<T> ts = new ArrayList<T>();
		
		return ts;
	}
	
}
